/*
 * ReplacementSelection.cpp
 *
 *  Created on: Oct 7, 2012
 *      Author: romina
 */

#include "ReplacementSelection.h"

namespace ordenamiento {

ReplacementSelection::ReplacementSelection(string path_input, string path_particiones) {
	this->bufferEscritura = new Buffer(TAM_BUFFER_ESCRITURA);
	this->bufferOrdenamiento = new Buffer(TAM_BUFFER_ORDENAMIENTO);
	this->bufferLectura = new Buffer(TAM_BUFFER_LECTURA);
	this->file_input = new ArchivoBinario(path_input, R);
	this->file_log = new ArchivoDeTexto("informe_particiones.txt", W);
	this->path_particiones = path_particiones;
	this->heap = NULL;
	this->particion = NULL;
	this->cant_part = 0;
}

void ReplacementSelection::llenar_buffer_lectura(){
	this->bufferLectura->vaciar();
	for (int i = 0; i < N; i++){
		if (this->file_input->is_EOF()){
			break;
		}
		this->bufferLectura->addDato(this->file_input->obtenerLinea());
	}
}

void ReplacementSelection::llenar_buffer_ordenamiento(){
	for (int i = 0; i < 10; i++){
		llenar_buffer_lectura();
		for (int j=0; j< this->bufferLectura->getCantidad() ; j++){
			this->bufferOrdenamiento->addDato(this->bufferLectura->getDato(j));
		}
	}
}

void ReplacementSelection::addToParticionFromBufferEscritura(bool is_last = false){
	for (int i = 0; i < this->bufferEscritura->getCantidad(); i++){
		if(is_last && i == this->bufferEscritura->getCantidad() - 1)
			this->particion->escribirPalabra(this->bufferEscritura->getDato(i));
		else
			this->particion->escribirLinea(this->bufferEscritura->getDato(i));

		this->particion->incRegistros();
	}
	this->bufferEscritura->vaciar();
}

void ReplacementSelection::crear_particion(){

	if (this->cant_part == 0){
		//se crea un particion por primera vez
		//creo una carpeta para contenerlas
		Directorio::crear_carpeta("", this->path_particiones);
	}

	string path = "";
	path.append(this->path_particiones);
	path.append("//");
	path.append("particion");
	path.append(Palabra::intToString(this->cant_part));
	path.append(".bin");
	this->particion = new Particion(path, this->cant_part);
	this->file_log->escribirLinea("-------------------");
	this->file_log->escribirLinea("particion numero " + Palabra::intToString(this->cant_part));
	this->cant_part++;

}


void ReplacementSelection::cerrar_particion(){
	this->file_log->escribirLinea("registros: " + Palabra::intToString(this->particion->getCantidadRegistros()));
	//se cierra la particion abierta
	this->particion->close();
}


void ReplacementSelection::replace_selection(){
	int RAIZ = 0;
		int registrosLeidos = 0;
		string minimo;
		string next;
		try{
			this->file_log->escribirLinea("ORDENAMIENTO EXTERNO");
			this->file_log->escribirLinea("");

			llenar_buffer_ordenamiento();
			this->heap = new Heap(this->bufferOrdenamiento);
			this->heap->buildheap();

			this->crear_particion();

			minimo = this->heap->getRaiz();
			this->bufferEscritura->addDato(minimo);

			do{
				llenar_buffer_lectura();
				registrosLeidos = this->bufferLectura->getCantidad();
				if (registrosLeidos > 0){
					//se lee desde el buffer de lectura
					for(int i = 0; i < this->bufferLectura->getCantidad(); i++){
						next = this->bufferLectura->getDato(i);

						//***congelamos y achicamos el heap***
						if(next.compare(minimo) < 0){
							this->heap->swapRaizLast();
							this->heap->setValue(this->heap->getLast(), next);
							this->heap->decreaseLast();

							//***estan todos los elementos congelados***
							if(this->heap->getLast() == -1){
								this->addToParticionFromBufferEscritura(true);
								this->cerrar_particion();
								this->crear_particion();
								this->heap->restaurarLast();

								this->heap->buildheap();
							}
						//***guardamos el registro leido en la raiz***
						}else{
							this->heap->setValue(RAIZ, next);
						}
						this->heap->siftdown(RAIZ);
						minimo = this->heap->getRaiz();
						if (this->bufferEscritura->getCantidad() == this->bufferEscritura->getCapacidad()){
							this->addToParticionFromBufferEscritura(false);
						}
						this->bufferEscritura->addDato(minimo);
					}
					this->addToParticionFromBufferEscritura(false);
				}
				else{

					int no_congelados = 0;

					while(this->heap->getLast() > 0){
						this->heap->swapRaizLast();
						this->heap->decreaseLast();

						this->heap->siftdown(RAIZ);
						minimo = this->heap->getRaiz();
						if (this->bufferEscritura->getCantidad() == this->bufferEscritura->getCapacidad()){
							this->addToParticionFromBufferEscritura(false);
						}
						this->bufferEscritura->addDato(minimo);

						no_congelados++;
					}//end while - mientras haya elementos en el heap

					this->addToParticionFromBufferEscritura(true);
					this->cerrar_particion();


					//se despiertan los congelados
					if(no_congelados < this->heap->getSize()){
						this->crear_particion();
						this->heap->restaurarLast();
						this->heap->shiftLeft(no_congelados);

						for(int j = 0; j < no_congelados; j++){
							this->heap->decreaseLast();
						}

						this->heap->buildheap();
						while(this->heap->getLast() > 0){
							minimo = this->heap->getRaiz();
							if (this->bufferEscritura->getCantidad() == this->bufferEscritura->getCapacidad())
									this->addToParticionFromBufferEscritura(false);
							this->bufferEscritura->addDato(minimo);
							this->heap->swapRaizLast();
							this->heap->decreaseLast();
							this->heap->siftdown(RAIZ);
						}
						this->addToParticionFromBufferEscritura(true);
						this->cerrar_particion();
					}//end if hay elementos congelados

				}//end else - se ordenan los no congelados

			}while(registrosLeidos > 0);



		}catch(exception& e){
			throw e;
		}
}

int ReplacementSelection::get_cant_particiones(){
	return this->cant_part;
}

ReplacementSelection::~ReplacementSelection() {
	delete(this->bufferEscritura);
	delete(this->bufferLectura);
	delete(this->bufferOrdenamiento);
	delete(this->heap);
	delete(this->file_input);
	delete(this->file_log);
}

} /* namespace ordenamiento */
